//
// Created by LONG on 2020/10/21.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "assert.h"
#include "../../include/utils/memPoolSet.h"
#include "../../include/utils/misc.h"

typedef struct MemPoolAttrIner
{
    uint32_t size; /* 块大小 */
    uint32_t count; /* 块数 */
    S_MemPool *pMemPool;
} S_MemPoolAttrIner;

typedef void* (*MPS_GetFun)(uint32_t type, uint32_t size, const char* func, uint32_t line);

typedef struct MemPoolSetAttrIner
{
    MPS_GetFun mpGet;
    uint32_t memSizeBase;
    uint32_t memSizeBaseShift; //把除法变成移位
    uint32_t memSizeMax;
    uint32_t memPoolMode;
    uint32_t memPoolNum;
    S_MemPoolAttrIner *pMemPoolAttrIners;
} S_MemPoolSetAttrIner;

typedef struct MemPool
{
    uint32_t memPoolSetNum;
    S_MemPoolSetAttrIner *pMemPoolSetAttrIners;
} S_MemPoolSet;

static S_MemPoolSet gMemPoolSet = {0};

static volatile uint32_t gMemPoolGetCount = 0;
static volatile uint32_t gMemPoolPutCount = 0;

void *MPS_GetNormal(uint32_t arrayIdx, uint32_t size, const char* func, uint32_t line);
void *MPS_GetMult(uint32_t arrayIdx, uint32_t size, const char* func, uint32_t line);
void *MPS_GetPower2(uint32_t arrayIdx, uint32_t size, const char* func, uint32_t line);
static const MPS_GetFun gMemPoolSetGetFun[] =
{
    MPS_GetNormal, MPS_GetMult, MPS_GetPower2
};
STATIC_ASSERT(ARRAY_SIZE(gMemPoolSetGetFun) == MP_MODE_MAX);

static int32_t MPS_CheckExt(uint32_t idx, uint32_t mmpMode, S_MemPoolAttr memPoolAttrArray[], uint32_t arrayNum)
{
    uint32_t loop;
    uint32_t baseSize;

    baseSize = memPoolAttrArray[0].size;
    if (!IS_POWER2(baseSize))
    {
        printf("MPS_CheckExt(%d, %d) baseSize[%d] is not power of 2!\n", idx, mmpMode, baseSize);
        return CSP_RC_ER;
    }

    switch (mmpMode)
    {
    case MP_MODE_NORMAL:
        for (loop = 0; loop < arrayNum; loop++)
        {
            if (memPoolAttrArray[loop].size < baseSize)
            {
                printf("MPS_CheckExt(%d, %d) memPoolArray[%d].size[%d] error!\n", idx, mmpMode, loop, memPoolAttrArray[loop].size);
                return CSP_RC_ER;
            }
            baseSize = memPoolAttrArray[loop].size;
        }
        break;
    case MP_MODE_MULT:
        for (loop = 0; loop < arrayNum; loop++)
        {
            if (memPoolAttrArray[loop].size != baseSize * (loop + 1))
            {
                printf("MPS_CheckExt(%d, %d) memPoolArray[%d].size[%d] error!\n", idx, mmpMode, loop, memPoolAttrArray[loop].size);
                return CSP_RC_ER;
            }
        }
        break;
    case MP_MODE_POWER2:
        for (loop = 0; loop < arrayNum; loop++)
        {
            if (memPoolAttrArray[loop].size != baseSize * ((uint32_t)1 << loop))
            {
                printf("MPS_CheckExt(%d, %d) memPoolArray[%d].size[%d] error!\n", idx, mmpMode, loop, memPoolAttrArray[loop].size);
                return CSP_RC_ER;
            }
            if (((uint32_t)1 << loop) >= LOG2X_TABLE_SIZE)
            {
                printf("MPS_CheckExt(%d, %d) arrayNum[%d] error!\n", idx, mmpMode, arrayNum);
                return CSP_RC_ER;
            }
        }
        break;
    default:
        printf("MPS_CheckExt(%d, %d) mmpMode[%d] error!\n", idx, mmpMode, mmpMode);
        return CSP_RC_ER;
    }

    return CSP_RC_OK;
}

static int32_t MPS_Check(S_MemPoolSetAttr memPoolSetArray[], uint32_t arrayNum)
{
    int32_t rc;
    uint32_t loop;

    for (loop = 0; loop < arrayNum; loop++)
    {
        rc = MPS_CheckExt(loop,
            memPoolSetArray[loop].memPoolMode,
            memPoolSetArray[loop].pMemPoolAttrs,
            memPoolSetArray[loop].memPoolAttrNum);
        if (CSP_RC_OK != rc)
        {
            return CSP_RC_ER;
        }
    }
    return CSP_RC_OK;
}

int32_t MPS_Init(S_MemPoolSetAttr memPoolSetAttrArray[], uint32_t arrayNum)
{
    int32_t rc;
    uint32_t loop;
    uint32_t inerLoop;
    S_MemPoolAttr *pMemPoolAttr;
    S_MemPoolAttrIner *pMemPoolAttrIner;
    S_MemPoolSetAttr *pMemPoolSetAttr;
    S_MemPoolSetAttrIner *pMemPoolSetAttrIner;

    rc = MPS_Check(memPoolSetAttrArray, arrayNum);
    if (CSP_RC_OK != rc)
    {
        return CSP_RC_ER;
    }
    gMemPoolSet.memPoolSetNum = arrayNum;
    gMemPoolSet.pMemPoolSetAttrIners = OS_MemCalloc(1, sizeof(S_MemPoolSetAttrIner) * arrayNum);
    if (NULL == gMemPoolSet.pMemPoolSetAttrIners)
    {
        return CSP_RC_ER;
    }
    for (loop = 0; loop < arrayNum; loop++)
    {
        pMemPoolSetAttr = &memPoolSetAttrArray[loop];
        pMemPoolSetAttrIner = &gMemPoolSet.pMemPoolSetAttrIners[loop];

        pMemPoolSetAttrIner->mpGet = gMemPoolSetGetFun[pMemPoolSetAttr->memPoolMode];
        pMemPoolSetAttrIner->memSizeBase = pMemPoolSetAttr->pMemPoolAttrs[0].size;
        pMemPoolSetAttrIner->memSizeBaseShift = 0;
        while ((1 << pMemPoolSetAttrIner->memSizeBaseShift) < pMemPoolSetAttrIner->memSizeBase)
        {
            pMemPoolSetAttrIner->memSizeBaseShift++;
        }
        pMemPoolSetAttrIner->memSizeMax = pMemPoolSetAttr->pMemPoolAttrs[pMemPoolSetAttr->memPoolAttrNum - 1].size;
        pMemPoolSetAttrIner->memPoolMode = pMemPoolSetAttr->memPoolMode;
        pMemPoolSetAttrIner->memPoolNum = pMemPoolSetAttr->memPoolAttrNum;
        pMemPoolSetAttrIner->pMemPoolAttrIners = OS_MemCalloc(1, sizeof(S_MemPoolAttrIner) * pMemPoolSetAttr->memPoolAttrNum);
        if (NULL == pMemPoolSetAttrIner->pMemPoolAttrIners)
        {
            return CSP_RC_ER;
        }
        for (inerLoop = 0; inerLoop < pMemPoolSetAttr->memPoolAttrNum; inerLoop++)
        {
            pMemPoolAttr = &pMemPoolSetAttr->pMemPoolAttrs[inerLoop];
            pMemPoolAttrIner = &pMemPoolSetAttrIner->pMemPoolAttrIners[inerLoop];

            pMemPoolAttrIner->size = pMemPoolAttr->size;
            pMemPoolAttrIner->count = pMemPoolAttr->count;
            pMemPoolAttrIner->pMemPool = MP_Create(pMemPoolAttr->count, pMemPoolAttr->size, pMemPoolSetAttr->memPoolType);
            if (NULL == pMemPoolAttrIner->pMemPool)
            {
                return CSP_RC_ER;
            }
        }
    }

    return CSP_RC_OK;
}

void MPS_Final()
{
    uint32_t loop;
    uint32_t inerLoop;
    S_MemPoolSetAttrIner *pMemPoolSetAttrIner;

    for (loop = 0; loop < gMemPoolSet.memPoolSetNum; loop++)
    {
        pMemPoolSetAttrIner = &gMemPoolSet.pMemPoolSetAttrIners[loop];
        for (inerLoop = 0; inerLoop < pMemPoolSetAttrIner->memPoolNum; inerLoop++)
        {
            MP_Destroy(pMemPoolSetAttrIner->pMemPoolAttrIners[inerLoop].pMemPool);
        }
        OS_MemFree(pMemPoolSetAttrIner->pMemPoolAttrIners);
    }
    OS_MemFree(gMemPoolSet.pMemPoolSetAttrIners);

    memset(&gMemPoolSet, 0, sizeof(gMemPoolSet));
}

static inline void *MPS_GetExt(uint32_t arrayIdx, uint32_t size, uint32_t startIdx, const char* func, uint32_t line)
{
    void *ptr;
    uint32_t loop;

    ptr = NULL;
    for (loop = startIdx; loop < gMemPoolSet.pMemPoolSetAttrIners[arrayIdx].memPoolNum; loop++)
    {
        if (likely(gMemPoolSet.pMemPoolSetAttrIners[arrayIdx].pMemPoolAttrIners[loop].size >= size))
        {
            ptr = MP_Get(gMemPoolSet.pMemPoolSetAttrIners[arrayIdx].pMemPoolAttrIners[loop].pMemPool, size, func, line);
            if (likely(NULL != ptr))
            {
                gMemPoolGetCount++;
                break;
            }
        }
    }
    return ptr;
}

void *MPS_GetNormal(uint32_t arrayIdx, uint32_t size, const char* func, uint32_t line)
{
    return MPS_GetExt(arrayIdx, size, 0, func, line);
}

void *MPS_GetMult(uint32_t arrayIdx, uint32_t size, const char* func, uint32_t line)
{
    uint32_t index;

    index = (size - 1) >> gMemPoolSet.pMemPoolSetAttrIners[arrayIdx].memSizeBaseShift;
    return MPS_GetExt(arrayIdx, size, index, func, line);
}

void *MPS_GetPower2(uint32_t arrayIdx, uint32_t size, const char* func, uint32_t line)
{
    uint32_t xValue;

    xValue = size >> gMemPoolSet.pMemPoolSetAttrIners[arrayIdx].memSizeBaseShift;
    return MPS_GetExt(arrayIdx, size, gLog2xTable[xValue], func, line);
}

void* MPS_Get(uint32_t arrayIdx, uint32_t size, const char* func, uint32_t line)
{
    if (unlikely(size > gMemPoolSet.pMemPoolSetAttrIners[arrayIdx].memSizeMax))
    {
        return NULL;
    }
    return gMemPoolSet.pMemPoolSetAttrIners[arrayIdx].mpGet(arrayIdx, size, func, line);
}

int32_t MPS_Put(void *chunk)
{
    gMemPoolPutCount++;
    return MP_Put(chunk);
}

void MPS_Dump(void *buffer, uint32_t size)
{
    snprintf(buffer, size, "MPS_Dump : MemPoolGetCount[%u], MemPoolPutCount[%u].",
        gMemPoolGetCount, gMemPoolPutCount);
}

void MPS_ResetCounter()
{
    gMemPoolGetCount = 0;
    gMemPoolPutCount = 0;
}

uint32_t MPS_GetCount()
{
    return gMemPoolGetCount;
}

uint32_t MPS_PutCount()
{
    return gMemPoolPutCount;
}
